package com.todod.service;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import org.apache.commons.lang.StringUtils;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.core.CountRequest;
import org.elasticsearch.client.core.CountResponse;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.QueryStringQueryBuilder;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSONObject;
//import com.client.model.EsBase;
//import com.client.util.ConstantData;
import com.todod.common.util.ESData;
//import com.todod.exception.InaccurateStateException;
import com.todod.dto.EsSortDto;
import com.todod.entity.EsQuery;
import com.todod.entity.OrderBy;

/**
 * es管理service
 *
 */
@Service
public class ElasticsearchService {

	Logger log = LoggerFactory.getLogger(this.getClass());

	public final int timeOutMinutes = 10; // 超时时间分钟

	@Autowired
	private RestHighLevelClient restHighLevelClient;

	private String beginDateStr = "beginDate";
	private String endDateStr = "endDate";
	private String dateStr = "fileDate";
	private Map<String, String> queryDateMap = new HashMap<String, String>(); // 时间查询映射
	{

		queryDateMap.put(ESData.GRWD_INDEX + "-" + beginDateStr, "createtimeTi");
		queryDateMap.put(ESData.GRWD_INDEX + "-" + endDateStr, "createtimeTi");
		queryDateMap.put(ESData.BMWD_INDEX + "-" + beginDateStr, "createtimeTi");
		queryDateMap.put(ESData.BMWD_INDEX + "-" + endDateStr, "createtimeTi");
		queryDateMap.put(ESData.CKZX_INDEX + "-" + dateStr, "fileDate");
	}
	List<String> SortList = new ArrayList<String>();
	{
//		SortList.add("createtimeTi");

	}
	List<String> SelectList = new ArrayList<String>();
	{
		SelectList.add("radio");
		SelectList.add("type");
//		SelectList.add("starttime");
//		SelectList.add("zzName");
	}

	/**
	 * 查询
	 * 
	 * @Title: queryCount
	 * @Description:
	 * @author hyw
	 * @date 2020年5月14日
	 * @param index
	 * @param esQuery
	 * @param userId
	 * @param departId
	 * @return
	 * @throws IOException
	 */
	public SearchResponse query(String index, EsQuery esQuery, Long userId, Long departId) throws IOException {
		String key1 = esQuery.getKey();
		// 筛选
		Map<String, String> params = esQuery.getParams();// 筛选条件
		Map<String, String> terms = esQuery.getTerms();// terms 查询
		if (userId != null) {
			params.put("createuserId", userId.toString());
		}
		params.put("state", "1");
		params.put("type", esQuery.getType());
		params.put("departmentId", departId.toString());

		SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
		// 分页
		sourceBuilder.from((esQuery.getPage() - 1) * esQuery.getLimit());
		sourceBuilder.size(esQuery.getLimit());

		BoolQueryBuilder qb = QueryBuilders.boolQuery(); // 多条件查询

		if (terms != null && !terms.isEmpty()) {
			String values1 = terms.get("radio");
			if (values1 != null && StringUtils.isNotBlank(values1)) {
				// 精确后
				String key2 = esQuery.getKey();
				List<String> asList1 = Arrays.asList(key2.split(";"));
				String transkey1 = "";
				if (asList1.size() > 1) {
					for (int i = 0; i < asList1.size(); i++) {
						if (i > 0) {
							transkey1 += " \"" + asList1.get(i) + "\"";
						} else {
							transkey1 += " \"" + asList1.get(i) + "\"";
						}
					}
					if (values1.equalsIgnoreCase("1")) {
						qb.filter(QueryBuilders.termQuery("content", asList1));
					} else if (values1.equalsIgnoreCase("2")) {
						qb.filter(QueryBuilders.termQuery("fileName", asList1));
					} else if (values1.equalsIgnoreCase("3")) {
						qb.filter(QueryBuilders.termQuery("fileLabel", asList1));
					}
				} else {
					transkey1 += "\"" + asList1.get(0) + "\"";
					if (values1.equalsIgnoreCase("1")) {
						qb.filter(QueryBuilders.matchPhrasePrefixQuery("content", transkey1));
					} else if (values1.equalsIgnoreCase("2")) {
						qb.filter(QueryBuilders.matchPhrasePrefixQuery("fileName", transkey1));
					} else if (values1.equalsIgnoreCase("3")) {
						qb.filter(QueryBuilders.matchPhrasePrefixQuery("fileLabel", transkey1));
					}
				}
			}
			String values = terms.get("check");
			if (values != null && StringUtils.isNotBlank(values)) {
				if (values.indexOf("Word") != -1) {
					values = values.replace("Word", "docx,doc");
				}
				if (values.indexOf("PDF") != -1) {
					values = values.replace("PDF", "pdf");
				}
				if (values.indexOf("Excel") != -1) {
					values = values.replace("Excel", "xlsx,xls");
				}
				if (values.indexOf("PPT") != -1) {
					values = values.replace("PPT", "ppt,pptx");
				}
				if (values.indexOf("音频") != -1) {
					values = values.replace("音频", "mp3,flac,wav,flac");
				}
				if (values.indexOf("文本") != -1) {
					values = values.replace("文本", "txt");
				}
				if (values.indexOf("图片") != -1) {
					values = values.replace("图片", "png,jpeg,jpg");
				}
				if (values.indexOf("视频") != -1) {
					values = values.replace("视频", "mp4,mpeg,avi,navi,asf,mov,3gp,wmv,divx,xvid,rm,rmvb,flv,f4v");
				}
				String[] idArray = values.split(",");
				qb.filter(QueryBuilders.termsQuery("suffix", idArray));
			}
		} else {
			if (key1 != "" && key1 != null && key1 != "null" && StringUtils.isNotBlank(key1)) {
				System.out.println("转译前       " + key1);
				String key = QueryParser.escape(key1);// 特殊字符转义
				System.out.println("转译hou    " + key);
				// 精确后
				List<String> asList = Arrays.asList(key1.split(";"));
				String transkey = "";
				if (asList.size() > 1) {
					for (int i = 0; i < asList.size(); i++) {
						if (i > 0) {
							transkey += " \"" + asList.get(i) + "\"";
						} else {
							transkey += "\"" + asList.get(i) + "\"";
						}
					}
				} else {
					transkey += "\"" + asList.get(0) + "\"";
				}
				System.out.println("精确后    " + transkey);

				QueryStringQueryBuilder queryStringQuery = QueryBuilders.queryStringQuery(transkey);

				qb.must(queryStringQuery);
			}
		}

		if (params != null) {
			Set<String> keySet = params.keySet();
			for (String object : keySet) {
				if (params.get(object) != null && !StringUtils.isBlank(params.get(object))
						&& !"null".equals(params.get(object))) {
					if ("beginDate".equals(object)) {
						// 获取查询字段
						String field = getQueryDateField(index + "-" + object);
						for (String sortword : SelectList) {
							if (field.equalsIgnoreCase(sortword)) {
								field += ".keyword";
								break;
							}
						}
						// 查询大于等于给定时间的数据
						RangeQueryBuilder gte = QueryBuilders.rangeQuery(field).gte(params.get(object));
						qb.must(gte);
					} else if ("endDate".equals(object)) {
						// 获取查询字段
						String field = getQueryDateField(index + "-" + object);
						for (String sortword : SelectList) {
							if (field.equalsIgnoreCase(sortword)) {
								field += ".keyword";
								break;
							}
						}
						// 查询大于等于给定时间的数据
						RangeQueryBuilder lte = QueryBuilders.rangeQuery(field).lte(params.get(object));
						qb.must(lte);
					} else {
						String value = params.get(object);
						value = QueryParser.escape(value);// 特殊字符转义

						MatchQueryBuilder mpq = QueryBuilders.matchQuery(object, value);
						qb.must(mpq);
					}
				}
			}
		}

		sourceBuilder.query(qb);

		// 排序
		List<OrderBy> orders = esQuery.getOrders();
		if (orders != null && orders.size() > 0) {
			for (OrderBy orderBy : orders) {
				SortOrder sortOrder = SortOrder.DESC;
				if (ESData.ASC.equals(orderBy.getOrder())) {
					sortOrder = SortOrder.ASC;
				}
				String field = orderBy.getField();
				for (String sortword : SortList) {
					if (field.equalsIgnoreCase(sortword)) {
						field += ".keyword";
						break;
					}
				}
				System.out.println(field);
				sourceBuilder.sort(new FieldSortBuilder(field).order(sortOrder)); // 排序
			}
		}

		// 高亮
		List<String> highlights = esQuery.getHighlights();
		if (highlights != null && highlights.size() > 0) {
			HighlightBuilder highlightBuilder = new HighlightBuilder();

			for (String field : highlights) {
				highlightBuilder.field(field);
			}

			highlightBuilder.requireFieldMatch(false); // 如果要多个字段高亮,这项要为false
			highlightBuilder.preTags("<span style=\"color:red\">"); // 高亮设置
			highlightBuilder.postTags("</span>");
			sourceBuilder.highlighter(highlightBuilder);
		}

		SearchRequest searchRequest = new SearchRequest(index);
		searchRequest.source(sourceBuilder);
		System.out.println(sourceBuilder.toString());
		SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

		return searchResponse;
	}

	/**
	 * 
	 * @param index
	 * @param key
	 * @return
	 * @throws IOException
	 */
	public long queryCount(String index, String key) throws IOException {
		key = QueryParser.escape(key);// 特殊字符转义
		// 精确后
		List<String> asList = Arrays.asList(key.split(";"));
		String transkey = "";
		if (asList.size() > 1) {
			for (int i = 0; i < asList.size(); i++) {
				if (i > 0) {
					transkey += " \"" + asList.get(i) + "\"";
				} else {
					transkey += "\"" + asList.get(i) + "\"";
				}
			}
		} else {
			transkey += "\"" + asList.get(0) + "\"";
		}
		System.out.println("统计精确后    " + transkey);
		QueryStringQueryBuilder queryStringQuery = QueryBuilders.queryStringQuery(transkey);
		System.out.println(queryStringQuery.toString());
		CountRequest countRequest = new CountRequest(index);
		countRequest.query(queryStringQuery);

		CountResponse countResponse = restHighLevelClient.count(countRequest, RequestOptions.DEFAULT);

		long count = countResponse.getCount();

		return count;
	}

	/**
	 * s所有文档
	 * 
	 * @param index s索引
	 * @return
	 * @throws IOException
	 */
	public Long total(String index) throws IOException {
		CountRequest countRequest = new CountRequest(index);
		CountResponse count = restHighLevelClient.count(countRequest, RequestOptions.DEFAULT);
		long count1 = count.getCount();
		return count1;
	}

	/**
	 * 插入文档
	 * 
	 * @Title: insertDocs
	 */
	public IndexResponse insertDocs(String index, String type, JSONObject paramJson) throws IOException {
		Map<String, Object> jsonMap = new HashMap<>();

		Set<String> keySet = paramJson.keySet();
		for (String object : keySet) {
			if (!StringUtils.isBlank(object)) {
				jsonMap.put(object, paramJson.get(object));
			}
		}
		IndexRequest request = new IndexRequest(index).source(jsonMap);

		IndexResponse response = restHighLevelClient.index(request, RequestOptions.DEFAULT);

		return response;
	}

	/**
	 * 修改文档
	 * 
	 */
	public UpdateResponse updateDocs(String index, String type, String id, JSONObject paramJson) throws IOException {
		Map<String, Object> jsonMap = new HashMap<>();

		Set<String> keySet = paramJson.keySet();
		for (String object : keySet) {
			if (!StringUtils.isBlank(object)) {
				jsonMap.put(object, paramJson.get(object));
			}
		}
		@SuppressWarnings("deprecation")
		UpdateRequest request = new UpdateRequest(index, type, id).doc(jsonMap);

		UpdateResponse updateResponse = restHighLevelClient.update(request, RequestOptions.DEFAULT);

		return updateResponse;
	}

	private String getQueryDateField(String key) {
		return queryDateMap.get(key);
	}

	public void esRefresh() throws IOException {
		ClusterHealthRequest request = new ClusterHealthRequest();

		ClusterHealthResponse response = restHighLevelClient.cluster().health(request, RequestOptions.DEFAULT);
		log.info(response.toString());
	}

	/**
	 * s es聚合排序
	 * 
	 * @param key    索引字段
	 * @param fields 聚合字段
	 * @return
	 * @author hyw
	 * @throws IOException
	 */
	public List<EsSortDto> getinfo3(String index, String key, String fields) throws IOException {
		SearchRequest searchRequest = new SearchRequest(index);
		SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
		// size
		searchSourceBuilder.size(0);
		BoolQueryBuilder qb = QueryBuilders.boolQuery(); // 多条件查询
		String escape = QueryParser.escape(key);
		// 精确查询转译
		List<String> asList = Arrays.asList(escape.split(";"));
		String transkey = "";
		if (asList.size() > 1) {
			for (int i = 0; i < asList.size(); i++) {
				if (i > 0) {
					transkey += " \"" + asList.get(i) + "\"";
				} else {
					transkey += "\"" + asList.get(i) + "\"";
				}
			}
		} else {
			transkey += "\"" + asList.get(0) + "\"";
		}
		qb.must(QueryBuilders.queryStringQuery(transkey));
		searchSourceBuilder.query(qb);
		// es聚合
		TermsAggregationBuilder field = AggregationBuilders.terms("group_by").field(fields).size(50);
		searchSourceBuilder.aggregation(field);
		System.out.println(searchSourceBuilder.toString());
		//
		searchRequest.source(searchSourceBuilder);
		SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
		Terms guobie = search.getAggregations().get("group_by");
		List<? extends Terms.Bucket> buckets = guobie.getBuckets();
		List<EsSortDto> dtos = new ArrayList<>();
		if (buckets.size() > 0) {
			// 取map
			HashMap<String, Long> map = buckets.stream().collect(
					Collectors.toMap(x -> x.getKeyAsString(), x -> x.getDocCount(), (x1, x2) -> x2, HashMap::new));
			// 取最大值
			Long max = Collections.max(map.entrySet().stream().map(e -> e.getValue()).collect(Collectors.toList()));
			// 处理value->百分比
			HashMap<String, Long> hashMap1 = map.entrySet().stream()
					.collect(Collectors.toMap(x -> x.getKey(),
							x -> new BigDecimal(x.getValue()).divide(new BigDecimal(max), 2, BigDecimal.ROUND_UP)
									.multiply(new BigDecimal(100)).longValue(),
							(x1, x2) -> x2, HashMap::new));
			// 排序
			LinkedHashMap<String, Long> collect = hashMap1.entrySet().stream()
					.sorted(Collections.reverseOrder(Map.Entry.comparingByValue()))
					.collect(Collectors.toMap(x -> x.getKey(), x -> x.getValue(), (x1, x2) -> x2, LinkedHashMap::new));

			// list
			dtos = collect.entrySet().stream().map(e -> new EsSortDto(e.getKey(), e.getValue(), map.get(e.getKey())))
					.collect(Collectors.toList());
		}

		return dtos;

	}

	public static String escape2(String s) {
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < s.length(); i++) {
			char c = s.charAt(i);
			// These characters are part of the query syntax and must be escaped
			if (c == '\\' || c == '+' || c == '-' || c == '!' || c == '(' || c == ')' || c == ':' || c == '^'
					|| c == '[' || c == ']' || c == '\"' || c == '{' || c == '}' || c == '~' || c == '*' || c == '?'
					|| c == '|' || c == '&' || c == '/') {
				sb.append('\\');

			}
			sb.append(c);
		}
		return sb.toString();
	}

	/**
	 * 查询
	 * 
	 * @Title: queryCount
	 * @Description:
	 * @author hyw
	 * @date 2020年5月14日
	 * @param index
	 * @param departId
	 * @param esQuery
	 * @param userId
	 * @return
	 * @throws IOException
	 */
	public long queryTypeFiles(String index, String date, Long departId) throws IOException {
		// 筛选
		Map<String, String> params = new HashMap<>();
		if (date != null && StringUtils.isNotBlank(date)) {
			params.put("beginDate", date + " 00:00:00");
			params.put("endDate", date + " 23:59:59");
		}
		params.put("paixu", "paixu2");
		params.put("departmentId", departId.toString());

		SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

		BoolQueryBuilder qb = QueryBuilders.boolQuery(); // 多条件查询

		String state = "1,2";
		String[] idArray = state.split(",");
		qb.filter(QueryBuilders.termsQuery("state", idArray));

		if (params != null) {
			Set<String> keySet = params.keySet();
			for (String object : keySet) {
				if (params.get(object) != null && !StringUtils.isBlank(params.get(object))
						&& !"null".equals(params.get(object))) {
					if ("beginDate".equals(object)) {
						// 获取查询字段
						String field = getQueryDateField(index + "-" + object);
						for (String sortword : SelectList) {
							if (field.equalsIgnoreCase(sortword)) {
								field += ".keyword";
								break;
							}
						}
						// 查询大于等于给定时间的数据
						RangeQueryBuilder gte = QueryBuilders.rangeQuery(field).gte(params.get(object));
						qb.must(gte);
					} else if ("endDate".equals(object)) {
						// 获取查询字段
						String field = getQueryDateField(index + "-" + object);
						for (String sortword : SelectList) {
							if (field.equalsIgnoreCase(sortword)) {
								field += ".keyword";
								break;
							}
						}
						// 查询大于等于给定时间的数据
						RangeQueryBuilder lte = QueryBuilders.rangeQuery(field).lte(params.get(object));
						qb.must(lte);
					} else if ("fileDate".equals(object)) {
						String value = params.get(object);

						MatchQueryBuilder mpq = QueryBuilders.matchQuery("date", value);
						qb.must(mpq);
					} else {
						String value = params.get(object);

						MatchQueryBuilder mpq = QueryBuilders.matchQuery(object, value);
						qb.must(mpq);
					}
				}
			}
		}
		sourceBuilder.query(qb);

		SearchRequest searchRequest = new SearchRequest(index);
		searchRequest.source(sourceBuilder);
		System.out.println(sourceBuilder.toString());
		SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
		long hits = searchResponse.getHits().getTotalHits().value;

		return hits;
	}

	/**
	 * 删除文档
	 * 
	 */
	public DeleteResponse deleteDoc(String index, String type, String id, JSONObject paramJson) {
		@SuppressWarnings("deprecation")
		DeleteRequest request = new DeleteRequest(index, type, id);

		// 创建client
		DeleteResponse response = null;
		try {
			response = restHighLevelClient.delete(request, RequestOptions.DEFAULT);

			restHighLevelClient.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			System.out.println(e);
		}

		return response;
	}

}
